﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Scheduling_Problem16_2
{
    public class Scheduler
    {
        public static Scheduler Instance
        {
            get 
            {
                if (_instance == null)
                    _instance = new Scheduler();
                return _instance; 
            }
        }
        public List<Task> AllTasks = new List<Task>();
        private static Scheduler _instance;
    }

    public class LowestReleaseTimeComparer : IComparer<Task>
    {
        int IComparer<Task>.Compare(Task obj1, Task obj2)
        {
            if (obj1.ReleaseTime > obj2.ReleaseTime)
                return 1;
            if (obj1.ReleaseTime == obj2.ReleaseTime)
                return 0;
            return -1;
        }
    }
    public class TimeRemainingComparer : IComparer<Task>
    {
        int IComparer<Task>.Compare(Task obj1, Task obj2)
        {
            if (obj1.TimeRemaining > obj2.TimeRemaining)
                return 1;
            if (obj1.TimeRemaining == obj2.TimeRemaining)
                return 0;
            return -1;
        }
    }
    public class CompletionFirst : IComparer<Task>
    {
        int IComparer<Task>.Compare(Task obj1, Task obj2)
        {
            if (obj1.CompletionTime > obj2.CompletionTime)
                return 1;
            if (obj1.CompletionTime == obj2.CompletionTime)
                return 0;
            return -1;
        }
    }
    public class Task : IComparable
    {
        public Task(int _duration)
        {
            Duration = _duration;
        }
        public Task(int _duration, int _releaseTime)
        {
            Duration = _duration;
            ReleaseTime = _releaseTime;
        }

        public int CompareTo( object obj)
        {
            Task obj2 = obj as Task;
            if (obj2.Duration < Duration)
                return 1;
            if (obj2.Duration == Duration)
                return 0;
            return -1;
        }

        public override string ToString()
        {
            return "(a" + Index + "), p" + Duration + ", c" + CompletionTime + ", r"+ ReleaseTime + ", t" + TimeRemaining;
        }
        public int Duration = 0;
        public int ReleaseTime = 0;
        public int Index = 0;
        public int CompletionTime = 0;
        public int TimeRemaining = 0;
    }

    public class SchedulingFunctions
    {
        public static List<Task> OrderedTasks(List<Task> tasks)
        {
            bool ReleaseTime = false;
            foreach (Task t in tasks)
                if (t.ReleaseTime > 0)
                    ReleaseTime = true;

            if (ReleaseTime)
                return OrderedTasksReleaseTime(tasks);
            else 
                return OrderedTasksZeroReleaseTime(tasks);
        }
        public static List<Task> OrderedTasksReleaseTime(List<Task> tasks)
        {
            List<Task> releaseFirst = new List<Task>();
            List<Task> timeRemainingFirst = new List<Task>();
            List<Task> retList = new List<Task>();
            foreach (Task t_ret in tasks)
                retList.Add(t_ret);
            foreach (Task t_first in tasks)
            {
                releaseFirst.Add(t_first);
                t_first.TimeRemaining = t_first.Duration;
                
            }
            releaseFirst.Sort(new LowestReleaseTimeComparer());
            Console.WriteLine("Sorting tasks in release first");
            Task current = null;
            int Time = 0;
            while (releaseFirst.Count > 0 || timeRemainingFirst.Count > 0)
            {
                Time++;
                bool removed = false;
                if (releaseFirst.Count > 0 && releaseFirst[0].ReleaseTime <= Time)//if data is changing, or we have no target
                {
                    
                    while (releaseFirst.Count > 0 && releaseFirst[0].ReleaseTime <= Time)
                    {
                        timeRemainingFirst.Add(releaseFirst[0]);
                        releaseFirst.RemoveAt(0);
                        removed = true;
                    }
                    if (removed)
                    {
                        timeRemainingFirst.Sort(new TimeRemainingComparer());
                        if (timeRemainingFirst[0] != current)
                        {
                            current = timeRemainingFirst[0];
                            Console.WriteLine("Processing:" + current);
                        }
                    }
                }
                if (current == null && timeRemainingFirst.Count > 0)
                {
                    
                    current = timeRemainingFirst[0];
                    Console.WriteLine("Processing:" + current);
                }
                else if (current == null)
                    continue;
                current.TimeRemaining--;
                if (current.TimeRemaining == 0)
                {
                    timeRemainingFirst.Remove(current);
                    current.CompletionTime = Time;
                    Console.WriteLine("Finished @" + Time + " " + current );
                    current = null;
                }
            }
            retList.Sort(new CompletionFirst());
            return retList;
        }

        public static List<Task> OrderedTasksZeroReleaseTime(List<Task> tasks)
        {
            List<Task> retList = new List<Task>();
            foreach (Task t in tasks)
                retList.Add(t);
            retList.Sort();
            float avgTime = 0;
            for (int i = 0; i < retList.Count; i++)
            {
                avgTime += retList[i].Duration;
                retList[i].CompletionTime = (int)avgTime;
            }
            
            return retList;
        }
    }
}
